import Vue from './Vue.js'
import Dep from './Dep.js'
import Watcher from './Watcher.js'

export function observer(vm: Vue, obj: Record<string, any>): void {
    const dep = new Dep()

    Object.keys(obj).forEach((key) => {
        // 保存原来的值
        let internalValue = obj[key]
        Object.defineProperty(vm, key, {
            get(): any {
                if (Dep.target) {
                    dep.addSub(Dep.target)
                }
                return internalValue
            },
            set(newValue: any): void {
                internalValue = newValue;
                dep.notify()
            }
        })
    })
}

/**
 * 编译函数，用于将Vue实例的模板编译成真实DOM。
 * @param vm Vue实例，包含模板和数据。
 */
export function compile(vm: Vue): void {
    const el: HTMLElement | null = document.querySelector(vm.$el)
    if (!el) {
        throw new Error('Element with selector can not be found.')
    }

    const documentFragment: DocumentFragment = document.createDocumentFragment()

    const reg: RegExp = /\{\{(.*)\}\}/;

    while (el.firstChild) {
        
        const child = el.firstChild
        if (child.nodeType === Node.ELEMENT_NODE) {
            const element = child as HTMLElement
            if (reg.test(element.innerHTML)) {
                const vmKey = RegExp.$1.trim();
                new Watcher(vm, child, vmKey)
            } else {
                Array.from(element.attributes).forEach((attr) => {
                    if (attr.name === 'v-model') {
                        const vmKey = attr.value;
                        element.addEventListener('input', (ev: Event) => {
                            const target = ev.target as HTMLInputElement
                            vm[vmKey] = target.value;
                        })
                    }
                })
            }

        } else if (child.nodeType === Node.TEXT_NODE && reg.test(child.textContent || '')) {
            const vmKey = RegExp.$1.trim();
            new Watcher(vm, child, vmKey)
        }

        documentFragment.appendChild(child)
    }


    el.appendChild(documentFragment)
}
